package de.mmis.core.base.abstracttree.serializer.selector;

import java.lang.reflect.Array;
import java.util.Map;

import de.mmis.core.base.abstracttree.InnerNode;
import de.mmis.core.base.abstracttree.serializer.DeserializableException;
import de.mmis.core.base.abstracttree.serializer.SerializerHelper;
import de.mmis.core.base.gclass.GArray;
import de.mmis.core.base.gclass.GClass;

/**
 * {@link InstanceSelector} representing an abstract array type. The target type
 * determines the component type of the array
 * 
 * @author Christian Koch
 */
public class ArraySelector extends InstanceSelector {

	private final InnerNode struct;
	private final ClassLoader classLoader;

	public ArraySelector(InnerNode struct, ClassLoader classLoader, String id,
			Map<String, Object> idmap) {
		super(id, idmap);
		this.struct = struct;
		this.classLoader = classLoader;
	}

	@Override
	public Object createInstance(GClass gclass)
			throws InstanceSelectorException {

		GClass comptype;
		if (gclass instanceof GArray)
			comptype = ((GArray) gclass).getComponentType();
		else {
			Class<?> baseclass = gclass.getBaseClass();
			if (baseclass == Object.class)
				baseclass = Object[].class;
			if (!baseclass.isArray())
				throw new InstanceSelectorException(
						"cannot convert to classes other than arrays");
			comptype = GClass.fromClass(baseclass.getComponentType());
		}

		Object ret = Array.newInstance(comptype.getBaseClass(),
				this.struct.getNumberOfSubTrees());

		setID(ret);

		int index = 0;
		try {
			for (index = 0; index < this.struct.getNumberOfSubTrees(); index++) {
				Object curelem = SerializerHelper.deserializeObject(
						this.struct.getSubTree(index), comptype,
						this.classLoader, this.getIDMap());
				Array.set(ret, index, curelem);
			}
		} catch (DeserializableException e) {
			throw new InstanceSelectorException(
					"error while deserializing array element " + index, e);
		}
		return ret;
	}
}
